package com.ruoyi.flow.flow.instance;

import com.ruoyi.flow.common.Page;
import com.ruoyi.flow.core.exception.BadRequestException;
import com.ruoyi.flow.core.exception.NotFoundException;
import com.ruoyi.flow.flow.FlowConstants;
import com.ruoyi.flow.flow.instance.InstanceQueryParam;
import com.ruoyi.flow.flow.workflow.FlowActivity;
import com.ruoyi.flow.flow.workflow.FlowCondition;
import com.ruoyi.flow.flow.workflow.FlowService;
import com.ruoyi.flow.flow.workflow.FlowTask;
import com.google.common.base.Splitter;
import com.google.common.collect.LinkedListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import org.activiti.bpmn.model.*;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.history.*;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.PathVariable;

import javax.transaction.Transactional;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;

/**
 * Created by fenghuo on 2018/8/21.
 */
@Service
public class FlowInstanceService {
    private static final Logger LOGGER = LoggerFactory.getLogger(FlowInstanceService.class);
    @Autowired
    private HistoryService historyService;
    @Autowired
    private RuntimeService runtimeService;

    /**
     * 获取流程实例列表。可以用于获取我的申请
     *
     * @param page 分页信息
     * @return
     */
    public Page<FlowTask> instanceList(Page<FlowTask> page, InstanceQueryParam queryParam) {
        List<HistoricProcessInstance> processInstances = null;
        HistoricProcessInstanceQuery instanceQuery = historyService.createHistoricProcessInstanceQuery().startedBy(queryParam.getUserId()).notDeleted();
        if (queryParam.getStatus() == FlowConstants.FlowStatus.FINISHED) {
            instanceQuery = instanceQuery.finished();
        } else if (queryParam.getStatus() == FlowConstants.FlowStatus.UNFINISHED) {
            instanceQuery = instanceQuery.unfinished();
        }
        if (page == null) {
            page = new Page<>();
            page.setOrder(Page.ORDER_DIRECTION_ASC);
            page.setSort(Page.DEFAULT_SORT);
        }
        //执行排序
        instanceQuery = page.executeSortAndOrderMethod(instanceQuery);
        // 执行分页
        processInstances = page.executePage(instanceQuery, HistoricProcessInstance.class);
        // 结果适配为FlowTask对象
        List<FlowTask> flowTasks = Lists.newLinkedList();
        for (HistoricProcessInstance processInstance : processInstances) {
            flowTasks.add(new FlowTask(processInstance));
        }
        page.setRows(flowTasks);
        return page;
    }

    /**
     * 获取流程办理历史
     *
     * @param instanceId
     * @param page
     * @return
     */
    public Page<FlowTask> getTaskHistoryByProcessInstanceId(@PathVariable String instanceId, Page<FlowTask> page) {
        HistoricActivityInstanceQuery query = historyService.createHistoricActivityInstanceQuery().processInstanceId(instanceId);
        List<HistoricActivityInstance> taskInstances = Lists.newLinkedList();
        List<FlowTask> flowTasks = Lists.newLinkedList();
        if (page == null) {
            page = new Page<>();
        }
        page.setTotal(query.count());
        query.orderByHistoricActivityInstanceStartTime().asc();
        if (page.getPageNumber() != -1 && page.getPageSize() != -1) {//需要分页
            taskInstances = query.listPage(page.getOffset(), page.getLimit());
        } else {
            taskInstances = query.list();
        }

        if (!Objects.isNull(taskInstances) && !taskInstances.isEmpty()) {
            Multimap<String, FlowTask> multimap = LinkedListMultimap.create();
            for (HistoricActivityInstance taskInstance : taskInstances) {
                //跳过网关
                FlowNode flowNode = FlowService.activityService().getFlowNode(taskInstance.getProcessDefinitionId(), taskInstance.getActivityId());
                if (flowNode instanceof StartEvent && !(flowNode.getParentContainer() instanceof SubProcess)) {
                    flowTasks.add(new FlowTask(taskInstance));
                } else if (flowNode instanceof EndEvent && !(flowNode.getParentContainer() instanceof SubProcess)) {
                    flowTasks.add(new FlowTask(taskInstance));
                } else if (flowNode instanceof UserTask) {
                    flowTasks.add(new FlowTask(taskInstance));
                }
            }
            for (String key : multimap.keySet()) {
                flowTasks.addAll(multimap.get(key));
            }
        }
        page.setRows(flowTasks);
        return page;
    }

    @Transactional
    /**
     * 删除流程实例
     */
    public boolean deleteInstance(String instanceId) throws BadRequestException {
        checkArgument(StringUtils.isNotEmpty(instanceId), "参数instanceId不能为空.");
        try {
            ProcessInstance instance = FlowService.instanceService().getProcessInstanceByInstanceId(instanceId);
            if (instance != null) {
                runtimeService.deleteProcessInstance(instanceId, "DELETE");
            }
            HistoricProcessInstance historicProcessInstance = FlowService.instanceService().getHistoricProcessInstanceByInstanceId(instanceId);
            if (historicProcessInstance != null) {
                historyService.deleteHistoricProcessInstance(instanceId);
            }
            return true;
        } catch (BadRequestException e) {
            return false;
        }
    }

    /**
     * 流程实例挂起
     *
     * @param instanceId
     * @return
     */
    public boolean suspendInstance(String instanceId) {
        try {
            checkArgument(StringUtils.isNotEmpty(instanceId), "参数instanceId不能为空.");
            ProcessInstance instance = FlowService.instanceService().getProcessInstanceByInstanceId(instanceId);
            checkState(instance != null, "ID为{}的流程实例不存在.", instanceId);
            runtimeService.suspendProcessInstanceById(instanceId);
            return true;
        } catch (Exception e) {
            LOGGER.error("流程实例挂起失败", e);
            return false;
        }
    }

    /**
     * 激活流程实例
     *
     * @param instanceId
     * @return
     */
    public boolean activeInstance(String instanceId) {
        try {
            checkArgument(StringUtils.isNotEmpty(instanceId), "参数instanceId不能为空.");
            ProcessInstance instance = FlowService.instanceService().getProcessInstanceByInstanceId(instanceId);
            checkState(instance != null, "ID为{}的流程实例不存在.", instanceId);
            if (instance.isSuspended()) {
                runtimeService.activateProcessInstanceById(instanceId);
            } else {
                LOGGER.warn("流程实例没有被挂起，不需要激活");
            }
            return true;
        } catch (Exception e) {
            LOGGER.error("流程实例挂起失败", e);
            return false;
        }
    }

    /**
     * 根据流程定义ID获取流程实例集合
     *
     * @param definitionId
     * @return
     */
    public List<ProcessInstance> getProcessInstanceByDefinitionId(String definitionId) {
        checkArgument(StringUtils.isNotEmpty(definitionId), "definitionId must not be null.");
        return runtimeService.createProcessInstanceQuery().processDefinitionId(definitionId).list();
    }

    /**
     * 根据流程实例ID获取流程实例
     *
     * @param instanceId
     * @return
     */
    public ProcessInstance getProcessInstanceByInstanceId(String instanceId) {
        checkArgument(StringUtils.isNotEmpty(instanceId), "instanceId must not be null.");
        ProcessInstance instance = runtimeService.createProcessInstanceQuery().processInstanceId(instanceId).singleResult();
        return instance;
    }

    /**
     * 根据流程实例ID获取历史流程信息
     *
     * @param instanceId
     * @return
     */
    public HistoricProcessInstance getHistoricProcessInstanceByInstanceId(String instanceId) {
        return historyService.createHistoricProcessInstanceQuery().processInstanceId(instanceId).singleResult();
    }

    /**
     * 获取发起流程时的变量信息。
     * <br/>
     * 启动业务流程时，会将表单信息作为变量信息填写到流程变量中，此时可以通过该方法获取这些表单信息
     *
     * @param instanceId
     * @return
     */
    public Map<String, Object> getStartVariables(String instanceId) {
        List<HistoricVariableInstance> variableInstances = historyService.createHistoricVariableInstanceQuery().executionId(instanceId).list();
        Map<String, Object> variables = Maps.newHashMap();
        for (HistoricVariableInstance variableInstance : variableInstances) {
            variables.put(variableInstance.getVariableName(), variableInstance.getValue());
        }
        return variables;
    }

    /**
     * 启动流程
     *
     * @param definitionKey 流程定义标识
     * @param businessKey   业务标识
     * @param assignees     下一步办理人员，可为空
     * @return
     * @throws Exception
     */
    public ProcessInstance startProcess(String definitionKey, String businessKey, String assignees, Map<String, Object> variables) throws Exception {
        //获取流程定义
        ProcessDefinition processDefinition = FlowService.definitionService().getProcessDefinitionByDefinitionKey(definitionKey);
        if (processDefinition == null) {
            throw new NotFoundException("流程标识为 [" + definitionKey + "] 的流程定义不存在！");
        }
        //封装表单数据
        List<FlowCondition> flowConditions = Lists.newArrayList();
        FlowCondition flowCondition = null;
        for (Map.Entry<String, Object> entry : variables.entrySet()) {
            flowCondition = new FlowCondition();
            flowCondition.setVarName(entry.getKey());
            flowCondition.setVarValue(Objects.toString(entry.getValue()));
            flowConditions.add(flowCondition);
        }
        if (StringUtils.isNotEmpty(assignees)) {
            FlowActivity nextFlowActivity = FlowService.activityService().getNextActivityInfo(processDefinition.getId(), flowConditions);
            List<String> candidates = Splitter.on(",").splitToList(assignees);
            FlowService.taskService().setNextUserVariables(candidates, nextFlowActivity.getAssignee().getValue(), nextFlowActivity.getMultiInstanceVariableName(), variables);
        }
        //启动流程
        ProcessInstance instance = runtimeService.startProcessInstanceByKey(definitionKey, businessKey, variables);
        LOGGER.info("基于流程定义 {} 启动流程成功，新建实例ID是 {}", processDefinition.getName(), instance.getId());
        return instance;
    }
}
